goransnotesfandomcom-20200213-history
How to Read .slcio Files
From the LCIO users manual: There are a number of examples in the src directory - some examples for reading and accessing data can be found in: src/cpp/src/EXAMPLE anajob.cc dumpevent.cc recjob.cc readcalibration.cc src/cpp/src/IMPL LCTOOLS.cc File handling Before you can read from an LCIO file you have to create an instance of LCReader using LCFactory: LCReader* lcReader = LCFactory::getInstance()->createLCReader() ; The factory pattern is used to hide the concrete implementation of the data format (see 3.6) from user code. Now opening and closing a file is as simple as: lcReader->open( "my_data.slcio" ) ; // ... here we can read sth. from the file lcReader->close() ; As described in the section on exceptions (3.1.1) you could enclose the above code or parts of it in a try-catch block, e.g. try{ lcReader->open( "my_data.slcio" ) ; // ... lcReader->close() ; } catch(IOException& e){ cout << " Unable to read and analyze the LCIO file - " << e.what() << endl ; } if you want to do anything else in your application after what would have been done in " //... " failed. Reading from the file There are two different ways of reading data from an LCIO file. One is via 'readNextSomething()' methods, where Something is either the next RunHeader or the next Event. This way the user has control over what is being read from the file. On the other hand you can only read one type of records at a time as you don't know the exact number of events for each run. A simple event loop looks like this: LCEvent* evt; while( (evt = lcReader->readNextEvent()) != 0 ) { LCTOOLS::dumpEvent( evt ) ; nEvents ++ ; } For small applications and data files this is a reasonable way of analyzing data. The other way of reading LCIO data files is via a Listener (or Observer) pattern. In this approach you have to write your own analysis module(s) which implements the LCRunListener and LCEventListener interface and register those with the LCReader. When reading the data stream the corresponding registered modules are called depending on the type of the current record. By writing modules for distinct tasks, e.g. vertex track reconstruction, track finding, clustering algorithms etc. this already defines an application framework for reconstruction with the LCEvent as underlying data structure. For example you can define one analysis module as run and event listener: // class for processing run and event records class MyAnalysis : public LCRunListener, public LCEventListener{ public: MyAnalysis() ; ~MyAnalysis() ; void processEvent( LCEvent * evt ) ; void processRunHeader( LCRunHeader* run) ; void modifyEvent( LCEvent * evt ) { /* not needed */ ;} void modifyRunHeader(LCRunHeader* run){ /* not needed */ ;} //... }; Here the processEvent/RunHeader methods are used, as they provide read only access to the data. The only modification of the data allowed in read only mode is the addition of new collections to the event (as this doesn't alter existing information). This will probably suffice for most analysis and reconstruction applications. So unless you need to do any of the following: *remove collections from the event *change elements in the collections, i.e. fix bugs *add data to existing collections, e.g. background hits you should use the read only mode in the processRunHeader/Event methods which is also default for the readNextEvent/RunHeader methods. In an analysis job one could for example create histograms for every run in processRunHeader( LCRunHeader* run) and then fill the histograms in processEvent( LCEvent * evt ). The corresponding - simplified but complete - main program will then look something like this (in C++): #include "lcio.h" #include "IO/LCReader.h" #include "MyAnalysis.h" using namespace lcio ; int main(int argc, char** argv ){ LCReader* lcReader = LCFactory::getInstance()->createLCReader() ; lcReader->open( argv1 ) ; MyAnalysis myAnalysis ; lcReader->registerLCRunListener( &myAnalysis ) ; lcReader->registerLCEventListener( &myAnalysis ) ; lcReader->readStream() ; // read the whole stream ! lcReader->close() ; return 0; } A more elaborated example (C++) that defines a mini framework where you can specify analysis modules at runtime can be found in 4.1. Accessing the data Check the API documentation and the examples on how to access the data in the LCIO data structures. Mostly this is straightforward, e.g. printing run data is as easy as: LCRunHeader *runHdr ; ... cout << " Run : " << runHdr->getRunNumber() << " - " << runHdr->getDetectorName() << ": " << runHdr->getDescription() << endl ; In order to access the information stored in the event, you need to know the collection names that hold the relevant data as well as the type of the objects, e.g. to access the TPC hits one could write code like the following: LCCollection* col = evt->getCollection("TPCHits") ; int nHits = col->getNumberOfElements() ; for( int i=0 ; i< nHits ; i++ ){ TPCHit* hit = dynamic_cast( col->getElementAt( i ) ) ; cout << " hit " << i << " - charge: " << hit->getCharge() << " - time: " << hit->getTime() << endl ; ... } A few comments are in order: *'collection name:' LCIO stores collections under user defined names. In order to retrieve the collection from the data, you need to know this name. Usually the person that created a data file will also have provided some documentation on the content (and on collection names). NB: The underlying implementation of the LCIO data format is self documenting, i.e. information on collection names and types is included in the files. This is used in the anajob and dumpevent example programs - run either of those to print collection names and types of an unknown LCIO file. *'casts:' as LCIO uses 'untyped' collections, we have to down-cast from LCObject to the corresponding type, i.e. we need to know the type of the data. This is analogous to the Collection interface in Java. As C++ does not provide a common base class we had to introduce LCObject as a common base for event data in LCIO.